SM4算法介绍与代码实现

本文最后更新于:2023年12月17日 下午

SM4算法介绍

加密过程

SM4加密过程

  • 首先左侧描述了SM4加密的整体流程:
  1. 输入的一个消息分组为128bits,划分成4个小块,每个小块32bits
  2. 这四个小块经过32轮加密处理
  3. 加密处理后,再经过一次Permutation置换,得到最终加密结果
  • 右侧描述其中一轮的加密过程,其他轮函数加密过程是类似的,只是轮密钥不同:
  1. X_3 先后跟 X_2 、X_1 进行异或 —— ①
  2. ① 得到的结果再与轮密钥 RK_i 进行异或 —— ②
  3. ② 得到的记过为32bits,再分成4组8bits,每一组8bits 使用相同的S-Box进行替换,得到新的8bits —— ③
  4. ③ 得到的4组8bits重新拼接得到32bits,将这32bits复制成5份输入,第1份循环左移2位,第2份循环左移10位,第3份循环左移18位,第4份循环左移24位,第5份不改动直接输出 —— ④
  5. ④ 的得到的5份输出的32bits与X_0 一起进行异或 —— ⑤
  6. 最终得到输出 X’_3 就是 ⑤ 的输出,其他输出X’_0 = X_1 , X’_1 = X_2, X’_2 = X_3

用公式进行描述如下所示,其中S代表使用S-box进行替换,L表示线性函数执行循环左移的操作

$$
X’_0 = X_1 \
X’_1 = X_2 \
X’_2 = X_3
$$

$$
X’_3 =X_0⊕ L( S (X_3 ⊕ X_2 ⊕ X_1 ⊕ RK_i))
$$
$$
A=(a_0,a_1,a_2,a_3), B=(b_0,b_1,b_2,b_3) \ (b_0,b_1,b_2,b_3) = S(A)= (Sbox(a_0),Sbox(a_1),Sbox(a_2),Sbox(a_3))
$$
$$
B=(b_0,b_1,b_2,b_3), C (32bits) \ C = L(B) = B ⊕ (B<<2) ⊕(B<<10) ⊕(B<<18)⊕(B<<24)
$$

通过观察SM4加密过程可以看到,在一轮加密中,有3个输出完全等同于3个输入,只有一个输出需要经过一些复杂操作,即计算X'_3,所以在进行解密时,只需要计算原来的X_0,而这可以通过现有的输出得到的(具体操作见下面的解密过程)。

解密过程

  • 在了解了SM4一轮的加密过程后,不难得出一轮的解密过程,如下图所示

SM4解密过程

  • 同样左侧描述了SM4解密的整体流程:
  1. 输入的一个密文C为128bits,划分成4个小块,每个小块32bits
  2. 对这四个小块经过一次Permutation置换
  3. 对置换结果进行32轮类似的解密处理,得到最终的明文输出
  • 具体描述一轮的解密过程,进行32轮类似的解密操作,其中X’_0对应加密过程中的输入X_1,X’_1对应输入X_2,X’_2对应输入X_3,所以重点计算得到原来输入的X_0,下面直接用公式来表示计算过程

$$
X_1 = X’_0 \qquad X_2 = X’_1 \qquad X_3 = X’_2
$$

$$
X_0 =X’_3⊕ L( S (X’_2 ⊕ X’_1 ⊕ X’_0 ⊕ RK_i))
$$

其中L线性函数跟S-box置换 都跟加密过程中一致;但需要注意的是解密过程中,轮密钥的使用次序是(31, 30, …, 1,0),跟加密过程使用的轮密钥次序正好是相反的

轮密钥生成

轮密钥生成

  • 轮密钥生成过程如上图所示,其中K代表的是16字节的密钥,公式里的S用的和加密过程中相同的Sbox

C++实现SM4加密与解密

实现目标

  1. 使用C++实现SM4算法的加密和结果,实现包括ECB、CBC两种加密模式。并提供命令⾏接⼝对⼆进制⽂件 进⾏加/解密。
  2. 实现对png图像文件的加密,只对图像数据部分进行加密,使得对加密png文件后仍能以png格式打开

命令行格式说明

  • 生成的二进制可执行文件为 AppliedCryptography,共有以下几种可选配置
    • -enc:进行加密操作
    • -dec:进行解密操作
    • -mode:指定加密/解密操作,其后跟着ecb或者cbc(如果没有指定,默认为ecb模式)
    • -png:指定输入和输出文件为png格式(对png文件做特殊处理,只加密数据块部分);如果指定了,请保证文件的后缀名以.png格式结尾
    • -in:输入文件的路径,请使用相对路径
    • -out:输出文件的路径,请使用相对路径
    • -key:指定密钥,默认为”123456”
    • -iv:指定初始化向量,默认为”123456”
    • -test:如果指定了,执行测试函数

1. 配置的命令请用小写
2. 一些配置之后请紧跟对应的参数值
3. 输入和输出的路径请用相对路径

  • 参考的测试命令如下
1
2
3
4
5
6
# 测试cbc模式加密png文件
./AppliedCryptography -enc -mode cbc -png -in logo.png -out test.png -iv 123456 -key 123456
./AppliedCryptography -dec -mode cbc -png -in test.png -out dec_test.png -iv 123456 -key 123456
# 测试ecb加密文件
./AppliedCryptography -enc -mode ecb -in test.txt -out ecb_test.txt -iv 123456 -key 123456
./AppliedCryptography -dec -mode ecb -in ecb_test.txt -out dec_test.txt -iv 123456 -key 123456

代码结构说明

首先说明进行代码目录的说明,如下图所示:

代码目录结构

  • 项目使用CMake 构建,build目录是构建目录,build/output/bin下是对应可执行文件的输出目录,build/output/bin/test 目录下存放测试用的相关文件
  • Cmake-build-debug:使用CLion 编译构建时生成的目录
  • FirstWork:主要目录,sm4对应源代码是算法的具体实现;test对应的是测试相关方法,主要用于测试算法的正确性
  • hash-library:一个简单的第三方库,封装了hash操作和hex编码和解码等操作
  • util:其中util是一个工具类,主要封装了一些辅助函数
  • main.cpp:程序入口,主要实现了解析命令行参数,执行对应的操作

实现相关说明

  • sm4.hsm4.cpp中实现了SM4算法,具体实现逻辑只要是拆分成多个子函数

    • L线性函数和Sbox替换函数实现
    • 轮密钥生成
    • SM4EncRoundSM4DecRound实现一轮的加密和解密
    • EncryptionDecryption实现128bits输入的加密和解密
    • EncFileDecFile实现文件的加密和解密
    • EncPNGDecPNG实现对png格式文件的加密和解密
  • 需要注意的是,png图片文件,有自己的固定格式,主要为头部8字节固定签名,加其他数据块的结构,所以解析时要得到真正的数据块(IDAT),对这一部分进行加密,这样才能加密后依然能够以png方式打开,具体的PNG格式参考:PNG格式说明

  • 测试函数主要有4个

    • TestAll() :进行所有测试
    • TestEncDec(): 测试SM4 单次加解密128bits
    • TestEncDecFileECB(): 测试SM4 加密和解密文件 ECB模式
    • TestEncDecFileCBC(): 测试SM4 加密和解密文件 CBC模式
    • TestEncDecPNG(): 测试SM4 加密和解密PNG图片

代码已上传Github仓库:https://github.com/2017zhangyuxuan/AppliedCryptography

但是当前代码还存在问题是,大端和小端的问题没有彻底解决,如果对比一些官方实现,加密结果是不同的,因为时间原因没有能排查出来,只能作罢。所以仅仅是提供一些思路。

参考附录

SM4算法原理 :https://blog.csdn.net/bird_tp/article/details/105988468

SM4 实现参考:https://cryptopp.com/docs/ref/class_s_m4_1_1_base.html#details

png文件头_图片格式知识PNG_weixin_39672443的博客-CSDN博客

PNG格式说明


SM4算法介绍与代码实现
https://2017zhangyuxuan.github.io/2021/11/13/2021-11/2021-11-13 SM4/
作者
Zhang Yuxuan
发布于
2021年11月13日
许可协议